using System;
using PaulRz.Threading;

namespace PaulRz.Util.Patterns
{
    
    public interface ILayer
    {
        Type InputType {get; }
        Type OutputType { get; }
        object Input { get; set; }
        object Output { get; set; }
        int InputSize { get; }
        void Start();
        void Stop();
    }
    
    public sealed class PipelineLayer<TInput, TOutput> : ILayer
        where TInput:class 
        where TOutput:class 
    {
        public PipelineLayer(PipelineElement<TInput, TOutput>[] elements)
        {
            this.elements = elements;
            size = elements.Length;
        }

        public PipelineLayer(PipelineElement<TInput, TOutput>[] elements, SynchronizedQueue<TInput> inputQueue, SynchronizedQueue<TOutput> outputQueue) : this(elements)
        {
            InstallQueues(inputQueue, outputQueue);
        }
        
        private int size;
        private PipelineElement<TInput, TOutput>[] elements;
        private SynchronizedQueue<TInput> m_inputQueue;
        private SynchronizedQueue<TOutput> m_outputQueue;
        
        public object Input
        {
            get
            {
                return m_inputQueue;
            }
            set
            {
                m_inputQueue = value as SynchronizedQueue<TInput>;
                for(int i=0; i<size; ++i)
                {
                    elements[i].Input = m_inputQueue;
                }
            }
        }
        
        public object Output
        {
            get { return m_outputQueue; }
            set
            {
                m_outputQueue = value as SynchronizedQueue<TOutput>;
                for(int i=0; i<size; ++i)
                {
                    elements[i].Output = m_outputQueue;
                }
            }
        }

        public int InputSize
        {
            get { return m_inputQueue.Count; }
        }

        private void InstallQueues(SynchronizedQueue<TInput> inputQueue, SynchronizedQueue<TOutput> outputQueue)
        {
            m_inputQueue = inputQueue;
            m_outputQueue = outputQueue;
            for(int i=0; i<size; ++i)
            {
                elements[i].Input = inputQueue;
                elements[i].Output = outputQueue;
            }
        }

        public Type InputType
        {
            get { return typeof (TInput); }
        }

        public Type OutputType
        {
            get { return typeof (TOutput); }
        }

        public void Start()
        {
            for(int i=0; i<size; ++i)
            {
                elements[i].Start();
            }
        }
        
        public void Stop()
        {
            for(int i=0; i<size; ++i)
            {
                elements[i].Stop();
            }
        }

    }
}
